﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Threading;
using System.Configuration;


namespace BigFileSplit
{
    public partial class frmMain : Form
    {
        private OpenFileDialog opDialog = new OpenFileDialog();

        /// <summary>
        /// 根据utf8字符的首字节,获取utf8字符所占字节数
        /// </summary>
        /// <param name="ch"></param>
        /// <returns></returns>
        static int GetUtf8charByteNum(byte ch)
        {
            int byteNum = 0;
            if (ch >= 0xFC && ch < 0xFE)
                byteNum = 6;
            else if (ch >= 0xF8)
                byteNum = 5;
            else if (ch >= 0xF0)
                byteNum = 4;
            else if (ch >= 0xE0)
                byteNum = 3;
            else if (ch >= 0xC0)
                byteNum = 2;
            else if (0 == (ch & 0x80))
                byteNum = 1;
            return byteNum;
        }

        public frmMain()
        {
            InitializeComponent();
        }

        private void buttonSelFile_Click(object sender, EventArgs e)
        {

            DialogResult res = opDialog.ShowDialog();
            if (res != DialogResult.OK)
                return;
            Configuration config = System.Configuration.ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
            txtBoxFile.Text = opDialog.FileName;
            FileInfo fi = new FileInfo(opDialog.FileName);
            textBoxDstDir.Text = fi.DirectoryName;
            string noextFileName = Path.GetFileNameWithoutExtension(opDialog.FileName);
            textBoxSplitFileFront.Text = noextFileName;
            config.AppSettings.Settings["BigFilePath"].Value = fi.DirectoryName;
            config.Save();

        }

        /// <summary>
        /// 按每个文件大小分割文件函数
        /// </summary>
        /// <param name="file">完整路径文件名</param>
        /// <param name="splitFileSize">每个分割文件的大小</param>
        /// <param name="dstFold">目标文件夹</param>
        /// <param name="splitFileNameFront">分割文件名称前缀</param>
        /// <param name="isUTF8">是否为UTF8编码文件，由于UTF8编码是不固定字节编码，所以不能用字节数分割文件，而用字符数分割文件</param>
        /// <returns>返回是否成功</returns>
        private bool SpliteFileBySize(string file, int splitFileSize, string dstFold, string splitFileNameFront, bool isUTF8)
        {
            try
            {
                FileInfo fileInfo = new FileInfo(file);
                if (fileInfo.Length < splitFileSize)
                    return false;
                if (!Directory.Exists(dstFold))
                    return false;
                int steps = (int)(fileInfo.Length / splitFileSize);
                if (!isUTF8)
                    progressBar1.Maximum = steps;
                else
                    progressBar1.Maximum = steps / 2;   //UTF8编码文件不能确定分割出几个文件，UTF8编码1个字符占1-4个字节，故取一个中间值
                string filePath = Path.GetDirectoryName(file);
                string noextFileName = Path.GetFileNameWithoutExtension(file);
                if (!string.IsNullOrEmpty(splitFileNameFront))
                    noextFileName = splitFileNameFront;
                string extName = Path.GetExtension(file);
                string splitFileFormat = dstFold + @"\" + noextFileName + "_{0:d}" + extName;
                using (FileStream fs = new FileStream(file, FileMode.Open, FileAccess.Read))
                {
                    if (!isUTF8)
                    {
                        using (BinaryReader br = new BinaryReader(fs))
                        {
                            int couter = 1;
                            bool isReadingComplete = false;
                            while (!isReadingComplete)
                            {
                                if (couter <= progressBar1.Maximum)
                                    progressBar1.Value = couter;
                                string newFileName = string.Format(splitFileFormat, couter);
                                if (File.Exists(newFileName))
                                {
                                    File.Delete(newFileName);
                                }
                                byte[] input = br.ReadBytes(splitFileSize);
                                using (FileStream writeFs = new FileStream(newFileName, FileMode.Create))
                                {
                                    using (BinaryWriter bw = new BinaryWriter(writeFs))
                                    {
                                        bw.Write(input);
                                    }
                                }
                                isReadingComplete = (input.Length != splitFileSize);
                                if (!isReadingComplete)
                                {
                                    couter += 1;
                                }
                                Application.DoEvents();
                            }
                        }
                    }
                    else
                    {
                        using(StreamReader sr = new StreamReader(fs, Encoding.UTF8))
                        {
                            int couter = 1;
                            bool isReadingComplete = false;
                            char[] inputBuffer = new char[splitFileSize];
                            while(!isReadingComplete)
                            {
                                if (couter <= progressBar1.Maximum)
                                    progressBar1.Value = couter;
                                string newFileName = string.Format(splitFileFormat, couter);
                                if (File.Exists(newFileName))
                                {
                                    File.Delete(newFileName);
                                }
                                int readCharNum = sr.ReadBlock(inputBuffer, 0, splitFileSize);
                                if(readCharNum > 0)
                                {
                                    byte[] input = Encoding.UTF8.GetBytes(inputBuffer);
                                    using (FileStream writeFs = new FileStream(newFileName, FileMode.Create))
                                    {
                                        using (BinaryWriter bw = new BinaryWriter(writeFs))
                                        {
                                            bw.Write(input);
                                        }
                                    }
                                    couter += 1;
                                }
                                else
                                {
                                    isReadingComplete = true;
                                }
                                Application.DoEvents();
                            }
                        }
                    }
                }
                return true;
            }
            catch(Exception ex)
            {
                MessageBox.Show(ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return false;
            }
        }

        /// <summary>
        /// 按文件个数分割文件函数
        /// </summary>
        /// <param name="file">完整路径文件名</param>
        /// <param name="splitFileSize">分割文件的个数</param>
        /// <param name="dstFold">目标文件夹</param>
        /// <param name="splitFileNameFront">分割文件名称前缀</param>
        /// <returns>返回是否成功</returns>
        private bool SpliteFileByNum(string file, int fileNums, string dstFold, string splitFileNameFront)
        {
            try
            {
                if (fileNums <= 1)
                    return false;
                FileInfo fileInfo = new FileInfo(file);
                if (fileInfo.Length < 1024*10)  //小于10K的文件不进行分割
                    return false;
                int splitFileSize = (int)(fileInfo.Length / fileNums);
                if (splitFileSize <= 0)
                    return false;
                if (!Directory.Exists(dstFold))
                    return false;
                if (splitFileSize % 2 != 0)     //每个文件字节不是偶数则变为偶数，分割UNICODE（1个字符占用2字节）文件时保证每个字符的完整编码，UTF8是1个字符占用字节数在1-4个字节之间，无法判断
                    splitFileSize++;
                int moreBytes = (int)fileInfo.Length - (fileNums * splitFileSize);
                if (moreBytes < 0)
                    moreBytes = 0;
                int steps = fileNums;
                progressBar1.Maximum = steps;
                string filePath = Path.GetDirectoryName(file);
                string noextFileName = Path.GetFileNameWithoutExtension(file);
                if (!string.IsNullOrEmpty(splitFileNameFront))
                    noextFileName = splitFileNameFront;
                string extName = Path.GetExtension(file);
                string splitFileFormat = dstFold + @"\" + noextFileName + "_{0:d}" + extName;
                using (FileStream fs = new FileStream(file, FileMode.Open, FileAccess.Read))
                {
                    using (BinaryReader br = new BinaryReader(fs))
                    {
                        int couter = 1;
                        bool isReadingComplete = false;
                        while (!isReadingComplete)
                        {
                            if (couter <= progressBar1.Maximum)
                                progressBar1.Value = couter;
                            string newFileName = string.Format(splitFileFormat, couter);
                            if (File.Exists(newFileName))
                            {
                                File.Delete(newFileName);
                            }
                            if (couter == fileNums)     //最后一个文件把多出不能平均的字节加上，都放到最后一个文件
                                splitFileSize += moreBytes;
                            byte[] input = br.ReadBytes(splitFileSize);
                            using (FileStream writeFs = new FileStream(newFileName, FileMode.Create))
                            {
                                using (BinaryWriter bw = new BinaryWriter(writeFs))
                                {
                                    bw.Write(input);
                                }
                            }
                            isReadingComplete = (couter == fileNums);
                            if (!isReadingComplete)
                            {
                                couter += 1;
                            }
                            Application.DoEvents();
                        }
                    }
                }
                return true;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return false;
            }
        }

        private void btnSplit_Click(object sender, EventArgs e)
        {
            if (string.IsNullOrEmpty(txtBoxFile.Text))
                return;
            if (string.IsNullOrEmpty(textBoxFileSize.Text))
                return;
            bool bSuc = false;
            btnSplit.Enabled = false;
            if (rBtnFileSize.Checked)
            {
                int splitFileSize = 0;
                try
                {
                    switch(comBoxUnit.SelectedIndex)
                    {
                        case 0:
                            splitFileSize = Convert.ToInt32(textBoxFileSize.Text) * 1024 * 1024;
                            break;
                        case 1:
                            splitFileSize = Convert.ToInt32(textBoxFileSize.Text) * 1024;
                            break;
                        case 2:
                            splitFileSize = Convert.ToInt32(textBoxFileSize.Text);
                            break;
                        default:
                            splitFileSize = Convert.ToInt32(textBoxFileSize.Text) * 1024 * 1024;
                            break;
                    }
                }
                catch { btnSplit.Enabled = true; return; }
                if (splitFileSize < 1024)
                {
                    btnSplit.Enabled = true;
                    return;
                }
                bSuc = SpliteFileBySize(txtBoxFile.Text, splitFileSize, textBoxDstDir.Text, textBoxSplitFileFront.Text, swichUTF8.Active);
            }
            else
            {
                int splitFileNum = 0;
                try
                {
                    splitFileNum = Convert.ToInt32(textBoxFileNum.Text);
                }
                catch { btnSplit.Enabled = true; return; }
                if (splitFileNum < 2)
                {
                    btnSplit.Enabled = true;
                    return;
                }
                bSuc = SpliteFileByNum(txtBoxFile.Text, splitFileNum, textBoxDstDir.Text, textBoxSplitFileFront.Text);
            }
            if (bSuc)
                MessageBox.Show("分割文件成功！", "信息", MessageBoxButtons.OK, MessageBoxIcon.Information);
            else
                MessageBox.Show("分割文件失败！", "提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
            
            btnSplit.Enabled = true;
        }

        private void frmMain_Load(object sender, EventArgs e)
        {
            opDialog.InitialDirectory = Application.StartupPath;
            opDialog.FileName = "";
            string bigfilePath = ConfigurationManager.AppSettings["BigFilePath"];
            if (!string.IsNullOrEmpty(bigfilePath))
            {
                opDialog.InitialDirectory = bigfilePath;
            }
            comBoxUnit.SelectedIndex = 0;
        }


        private void rBtnFileSize_CheckedChanged(object sender, EventArgs e)
        {
            if (rBtnFileSize.Checked)
            {
                textBoxFileSize.Enabled = true;
                textBoxFileNum.Enabled = false;

            }
            else
            {
                textBoxFileSize.Enabled = false;
                textBoxFileNum.Enabled = true;
                swichUTF8.Active = false;
            }
        }

        private void rBtnFileNum_CheckedChanged(object sender, EventArgs e)
        {
            if (rBtnFileNum.Checked)
            {
                textBoxFileSize.Enabled = false;
                textBoxFileNum.Enabled = true;
                swichUTF8.Active = false;
            }
            else
            {
                textBoxFileSize.Enabled = true;
                textBoxFileNum.Enabled = false;
            }
        }

        private void btnSelDstDir_Click(object sender, EventArgs e)
        {
            FolderBrowserDialog folderDialog = new FolderBrowserDialog();
            if(string.IsNullOrEmpty(textBoxDstDir.Text))
                folderDialog.SelectedPath = textBoxDstDir.Text;
            if(folderDialog.ShowDialog() == DialogResult.OK)
            {
                textBoxDstDir.Text = folderDialog.SelectedPath;
            }
        }

        private void btnExit_Click(object sender, EventArgs e)
        {
            this.Close();
            Application.Exit();
        }

        private void swichUTF8_ValueChanged(object sender, bool value)
        {
            if(swichUTF8.Active)
            {
                rBtnFileSize.Text = "按文件字符数分割";
                lblSizeCaption.Text = "每个文件字符数：";
                comBoxUnit.Items.Clear();
                comBoxUnit.Items.Add("M个");
                comBoxUnit.Items.Add("K个");
                comBoxUnit.Items.Add("个");
            }
            else
            {
                rBtnFileSize.Text = "按文件大小分割";
                lblSizeCaption.Text = "每个文件大小：";
                comBoxUnit.Items.Clear();
                comBoxUnit.Items.Add("MB");
                comBoxUnit.Items.Add("KB");
                comBoxUnit.Items.Add("Byte");
            }
            comBoxUnit.SelectedIndex = 0;
        }
    }
}
